package com.hy.${packageCode}.shared.distribute.standalone;


import com.hy.${packageCode}.shared.distribute.DistObjectFactory;
import com.hy.${packageCode}.shared.distribute.cache.DistCache;
import com.hy.${packageCode}.shared.distribute.cache.StandaloneCache;
import com.hy.${packageCode}.shared.distribute.counter.DistCounter;
import com.hy.${packageCode}.shared.distribute.counter.StandaloneCounter;
import com.hy.${packageCode}.shared.distribute.election.DistLeaderElection;
import com.hy.${packageCode}.shared.distribute.election.StandaloneLeaderElection;
import com.hy.${packageCode}.shared.distribute.lock.DelegatingLock;
import com.hy.${packageCode}.shared.distribute.lock.DelegatingReadWriteLock;
import com.hy.${packageCode}.shared.distribute.lock.DistLock;
import com.hy.${packageCode}.shared.distribute.lock.DistReadWriteLock;
import com.hy.${packageCode}.shared.distribute.map.DistMap;
import com.hy.${packageCode}.shared.distribute.map.StandaloneMap;
import com.hy.${packageCode}.shared.distribute.rate.DistRateLimiter;
import com.hy.${packageCode}.shared.distribute.rate.StandaloneRateLimiter;
import com.hy.${packageCode}.shared.distribute.DistObjectFactory;
import com.hy.${packageCode}.shared.distribute.cache.DistCache;
import com.hy.${packageCode}.shared.distribute.cache.StandaloneCache;
import com.hy.${packageCode}.shared.distribute.counter.DistCounter;
import com.hy.${packageCode}.shared.distribute.counter.StandaloneCounter;
import com.hy.${packageCode}.shared.distribute.election.DistLeaderElection;
import com.hy.${packageCode}.shared.distribute.election.StandaloneLeaderElection;
import com.hy.${packageCode}.shared.distribute.lock.DelegatingLock;
import com.hy.${packageCode}.shared.distribute.lock.DelegatingReadWriteLock;
import com.hy.${packageCode}.shared.distribute.lock.DistLock;
import com.hy.${packageCode}.shared.distribute.lock.DistReadWriteLock;
import com.hy.${packageCode}.shared.distribute.map.DistMap;
import com.hy.${packageCode}.shared.distribute.map.StandaloneMap;
import com.hy.${packageCode}.shared.distribute.rate.DistRateLimiter;
import com.hy.${packageCode}.shared.distribute.rate.StandaloneRateLimiter;
import com.hy.${packageCode}.shared.distribute.DistObjectFactory;
import com.hy.${packageCode}.shared.distribute.cache.DistCache;
import com.hy.${packageCode}.shared.distribute.cache.StandaloneCache;
import com.hy.${packageCode}.shared.distribute.counter.DistCounter;
import com.hy.${packageCode}.shared.distribute.counter.StandaloneCounter;
import com.hy.${packageCode}.shared.distribute.election.DistLeaderElection;
import com.hy.${packageCode}.shared.distribute.election.StandaloneLeaderElection;
import com.hy.${packageCode}.shared.distribute.lock.DelegatingLock;
import com.hy.${packageCode}.shared.distribute.lock.DelegatingReadWriteLock;
import com.hy.${packageCode}.shared.distribute.lock.DistLock;
import com.hy.${packageCode}.shared.distribute.lock.DistReadWriteLock;
import com.hy.${packageCode}.shared.distribute.map.DistMap;
import com.hy.${packageCode}.shared.distribute.map.StandaloneMap;
import com.hy.${packageCode}.shared.distribute.rate.DistRateLimiter;
import com.hy.${packageCode}.shared.distribute.rate.StandaloneRateLimiter;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ConcurrentReferenceHashMap;

import java.time.Duration;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 单实例 优化, 基于JDK JUC 实现
 */
@Slf4j
public class StandaloneObjectFactory implements DistObjectFactory {
    private static final Duration CLEAN_SCHEDULE_PERIOD = Duration.ofHours(2);
    private final String id = UUID.randomUUID().toString();
    private final Map<String, Object> distObjs = new ConcurrentHashMap<>();
    private final ScheduledExecutorService cleanScheduler = Executors.newSingleThreadScheduledExecutor();

    public StandaloneObjectFactory() {
        cleanScheduler.scheduleWithFixedDelay(this::runCacheExpiredClean,
                CLEAN_SCHEDULE_PERIOD.toMinutes(), CLEAN_SCHEDULE_PERIOD.toMinutes(), TimeUnit.MINUTES);
    }

    protected void runCacheExpiredClean() {
        long cleanAt = System.currentTimeMillis();
        distObjs.values().stream().filter(StandaloneCache.class::isInstance)
                .map(StandaloneCache.class::cast).forEach(StandaloneCache::cleanExpired);
        if (System.currentTimeMillis() > cleanAt + CLEAN_SCHEDULE_PERIOD.toMillis()) {
            log.warn("The scheduler run cache expired clean time reach at {}", CLEAN_SCHEDULE_PERIOD);
        }
    }
    
    @Override
    @PreDestroy
    public void shutdown() {
        cleanScheduler.shutdownNow();
    }


    @Override
    public String id() {
        return id;
    }

    @Override
    public <K, V> DistMap<K, V> getMap(String name, boolean witLocalCache) {
        return (DistMap<K, V>) distObjs.computeIfAbsent(name + ":map",
                item -> new StandaloneMap<>(new ConcurrentHashMap<>()));
    }

    @Override
    public DistLock getLock(String name) {
        return (DistLock) distObjs.computeIfAbsent(name + ":lock",
                item -> new DelegatingLock(new ReentrantLock()));
    }

    @Override
    public DistReadWriteLock getReadWriteLock(String name) {
        return (DistReadWriteLock) distObjs.computeIfAbsent(name + ":read_write_lock",
                item -> new DelegatingReadWriteLock(new ReentrantReadWriteLock()));
    }

    @Override
    public DistCounter getCounter(String name) {
        return (DistCounter) distObjs.computeIfAbsent(name + ":counter",
                item -> new StandaloneCounter(new AtomicLong()));
    }

    @Override
    public <K, V> DistCache<K, V> getCache(String name, boolean withLocalCache) {
        return (DistCache<K, V>) distObjs.computeIfAbsent(name + ":cache",
                item -> new StandaloneCache<>(new ConcurrentReferenceHashMap<>()));
    }

    @Override
    public DistLeaderElection getLeaderElection(String name) {
        log.info("===========================进入distLeader");
        return (DistLeaderElection) distObjs.computeIfAbsent(name + ":leader_election",
                item -> new StandaloneLeaderElection(name));
    }

    @Override
    public DistRateLimiter getRateLimiter(String name) {
        return (DistRateLimiter) distObjs.computeIfAbsent(name + ":rate_limiter",
                item -> StandaloneRateLimiter.create());
    }
}
